#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/of_irq.h>
#include <linux/timer.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/cdev.h>
#include <linux/wait.h>
#include <linux/uaccess.h>


/*
keys{
    interrupt-parent = <&gpiof>;
    interrupts = <9 0>, <7 0>, <8 0>;
    interrupts-name = "key1", "key2" "key3";
    key-gpios = <&gpiof 9 0>, <&gpiof 7 0>, <&gpiof 8 0>
}
*/
MODULE_LICENSE("GPL");

typedef struct
{
    unsigned int m_btnIrqNum;       // 按键中断号
    const char* m_btnName;          // 按键名称
    struct gpio_desc* m_pinDesc;    // 引脚描述信息
    struct timer_list m_timer;      // 消抖
}btn_desc_t;

static btn_desc_t btns[3] = {0};

struct device_node* btnNode = NULL;
static dev_t btnDevNum;
static struct cdev* btnCdev = NULL;
static unsigned int keyValue = 0x00;
static struct class* btnCls;

atomic_t btnAtomic;
int btnReadIsReady = 0;
static struct wait_queue_head btnWqh;

int btn_fops_open(struct inode * inode, struct file * filePtr)
{
    if(!atomic_dec_and_test(&btnAtomic))// 为0返回true
    {
        atomic_inc(&btnAtomic);
        return -EBUSY;
    }
    return 0;
}

int btn_fops_release(struct inode * inode, struct file * filePtr)
{
    atomic_inc(&btnAtomic);
    return 0;
}

ssize_t btn_fops_read(struct file * filePtr, char __user * buf, size_t len, loff_t * offset)
{
    int failedByteCount = 0;

    if(filePtr->f_flags & O_NONBLOCK && !btnReadIsReady)
    {
        return -EAGAIN;
    }

    if(wait_event_interruptible(btnWqh, btnReadIsReady))
    {
        return -ERESTARTSYS;
    }

    if(len > sizeof(keyValue))
        len = sizeof(keyValue);

    failedByteCount = copy_to_user(buf, &keyValue, len);
    btnReadIsReady = 0;
    return len - failedByteCount;
}

static struct file_operations btnFops = 
{
    .owner = THIS_MODULE,
    .open = btn_fops_open,
    .read = btn_fops_read,
    .release = btn_fops_release
};

irqreturn_t btn_irq_handle(int irqNum, void* dev)
{
    btn_desc_t* data = (btn_desc_t*)dev;
    mod_timer(&data->m_timer, jiffies + HZ / 100);
    return IRQ_HANDLED;
}

void btn_timer_handle(struct timer_list* timer)
{
    unsigned int state = 0;
    btn_desc_t* data = container_of(timer,btn_desc_t,m_timer);

    state = gpiod_get_value(data->m_pinDesc);
    printk("%s is %s!\n", data->m_btnName, state? "release" : "pressed");

    if(!strcmp(data->m_btnName, "key1"))
    {
        keyValue = 0x10 + state;
    }
    else if(!strcmp(data->m_btnName, "key2"))
    {
        keyValue = 0x20 + state;
    }
    else
    {
        keyValue = 0x30 + state;
    }

    btnReadIsReady = 1;
    wake_up(&btnWqh);
}

static int __init btn_drv_init(void)
{
    int i = 0;

    btnNode = of_find_node_by_path("keys");

    for(; i < ARRAY_SIZE(btns); i++)
    {
        btns[i].m_btnIrqNum = irq_of_parse_and_map(btnNode, i);
        of_property_read_string_index(btnNode, "interrupts-name", i, &btns[i].m_btnName);
        request_irq(btns[i].m_btnIrqNum,btn_irq_handle,IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
        btns[i].m_btnName,&btns[i]);
        btns[i].m_pinDesc = gpiod_get_from_of_node(btnNode, "key-gpios", i, GPIOD_IN, btns[i].m_btnName);
        
        timer_setup(&btns[i].m_timer, btn_timer_handle, 0);
    }

    alloc_chrdev_region(&btnDevNum,0,1,"myBtnDevNum");
    btnCdev = cdev_alloc();
    cdev_init(btnCdev, &btnFops);
    cdev_add(btnCdev, btnDevNum, 1);

    btnCls = class_create(THIS_MODULE, "myBtnClass");
    device_create(btnCls, NULL, btnDevNum, NULL,"myBtnsDev");

    atomic_set(&btnAtomic, 1); 
    init_waitqueue_head(&btnWqh);
    return 0;
}

static void __exit btn_drv_exit(void)
{
    int i = 0;

    device_destroy(btnCls, btnDevNum);
    class_destroy(btnCls);

    cdev_del(btnCdev);
    unregister_chrdev_region(btnDevNum,1);
    for(; i < ARRAY_SIZE(btns); i++)
    {
        del_timer(&btns[i].m_timer);
        gpiod_put(btns[i].m_pinDesc);
        free_irq(btns[i].m_btnIrqNum,&btns[i]);
        of_node_put(btnNode);
    }
    
}

module_init(btn_drv_init);
module_exit(btn_drv_exit);